//
// Created by wll on 19-6-30.
//
#define TOPDOWN_PARSER  0
#define LR0_PARSER  1
#define LR1_PARSER  2

#define	WLL0INTERPRETER	0
#define	WLL1INTERPRETER	1
#define	WLL2INTERPRETER	2
#define	WLL3INTERPRETER	3

#include "TopDownParsers.h"
#include "LR1Parsers.h"
#include "Wll3Interpreter.h"
#include "Wll2Interpreter.h"
#include "Wll1Interpreter.h"
#include "Wll0Interpreter.h"

#include "CompilerManager.h"
#include <fstream>
#include "WllTrace.h"
#include "WllConfigure.h"
#include "WllInterpreter.h"
#include "WllString.h"

using namespace Wll::Tool;

const char* CompilerManager::TOPDOWNPARSER = "TOPDOWN_PARSER";
const char* CompilerManager::LR1PARSER = "LR1_PARSER";

CompilerManager::CompilerManager()
{
    this->LoadConfig();

    if (this->disk_cache_flag)
    {
        this->LoadSymbols();
    }

}

void CompilerManager::LoadConfig()
{
    char process_dir[Wll::Tool::LogInnerTypes::MAX_FILE_NAME_LENGTH];
    Wll::Util::GetProcessDir(process_dir, sizeof(process_dir));
    std::string config_file_name = std::string(process_dir) + "/../conf/wll.conf";

    Configure configure;
    configure.Load(config_file_name.c_str());

    if (!configure.Get("compiler_manager", "memory_cache_flag", this->memory_cache_flag))
    {
        this->memory_cache_flag = true;
    }
    if (!configure.Get("compiler_manager", "disk_cache_flag", this->disk_cache_flag))
    {
        this->disk_cache_flag = true;
    }
    if (!configure.Get("compiler_manager", "language_parser_type", this->language_parser_type))
    {
        this->language_parser_type = "";
    }
    if (!configure.Get("compiler_manager", "disk_cache_flag", this->disk_cache_flag))
    {
        this->disk_cache_flag = true;
    }
    if (!configure.Get("compiler_manager", "wll_grammar_version", this->wll_grammar_version))
    {
        this->wll_grammar_version = 3;
    }
}

CompilerManager::~CompilerManager()
{
    if (this->disk_cache_flag)
    {
        this->SaveSymbols();
    }
}

void CompilerManager::LoadSymbols()
{
    Symbols::Load();
}

void CompilerManager::SaveSymbols()
{
    Symbols::Save();
}

std::string CompilerManager::GetCacheBaseDir()
{
    char process_dir[Wll::Tool::LogInnerTypes::MAX_FILE_NAME_LENGTH];
    Wll::Util::GetProcessDir(process_dir, sizeof(process_dir));
    return std::string(process_dir) + "/../data/cache";
}

string CompilerManager::GetSymbolTableFileName() {
    return GetCacheBaseDir() + "/symbols.table";
}

std::string CompilerManager::GetCacheWllFileName(std::string compiler_id)
{
    return GetCacheBaseDir() + "/" + compiler_id + ".xyz";
}

string CompilerManager::GetCacheLR1FileName(string compiler_id)
{
    return GetCacheBaseDir() + "/" + compiler_id + ".lr";
}

CompilerId * CompilerManager::LookupGrammarId(string grammar_file_name)
{
    map<string, CompilerId>::iterator i = this->file_name_to_compiler_id.find(grammar_file_name);
    return i == this->file_name_to_compiler_id.end() ? nullptr : &i->second;
}

CompilerId * CompilerManager::GetGrammarId(string grammar_file_name)
{
    CompilerId* compiler_id = nullptr;
    if (this->memory_cache_flag)
    {
        compiler_id = this->LookupGrammarId(grammar_file_name);
    }

    if (compiler_id == nullptr)
    {
        compiler_id = new CompilerId();
        compiler_id->grammar_file_name = grammar_file_name;
        compiler_id->compiler_id = CaculateCompilerId(grammar_file_name);
        compiler_id->grammar_file_modify_time = GetLastModifyTime(grammar_file_name);
    }
    else
    {
        time_t last_modify_time = GetLastModifyTime(grammar_file_name);
        if (compiler_id->grammar_file_modify_time < last_modify_time)
        {
            //语法文件有修改，更新其摘要信息
            compiler_id->compiler_id = CaculateCompilerId(grammar_file_name);
            compiler_id->grammar_file_modify_time = last_modify_time;
        }
    }

    if (this->memory_cache_flag)
    {
        this->file_name_to_compiler_id.insert(make_pair(grammar_file_name, *compiler_id));
    }
    return compiler_id;
}

Compiler* CompilerManager::LookupCompiler(string compiler_id)
{
    map<string, Compiler*>::iterator i = this->compiler_map.find(compiler_id);
    return (i == this->compiler_map.end() ? nullptr : i->second);
}

string CompilerManager::GetBoostrapCompilerFileName()
{
    char grammar_filename[Wll::Tool::LogInnerTypes::MAX_FILE_NAME_LENGTH];
    Wll::Util::GetProcessDir(grammar_filename, sizeof(grammar_filename));
    std::string version;
    Wll::Util::Int2String(this->wll_grammar_version, version);

    strcat(grammar_filename,"/../");
    strcat(grammar_filename, GRAMMAR_FILE_NAME);
    strcat(grammar_filename, ".wll");
    strcat(grammar_filename, version.c_str());

    return grammar_filename;
}

Compiler* CompilerManager::CreateOrGetBoostrapCompiler()
{
    string boostrap_compiler_file_name = GetBoostrapCompilerFileName();
    return this->CreateOrGetCompiler(boostrap_compiler_file_name, nullptr);
}


Compiler* CompilerManager::GetCompiler(std::string compiler_grammar_file_name)
{
    Compiler* compiler = nullptr;
    CompilerId* compiler_id = this->GetGrammarId(compiler_grammar_file_name);

    if (this->memory_cache_flag)
    {
        compiler = this->LookupCompiler(compiler_id->compiler_id);
        if (compiler != nullptr)
        {
            INFO(compiler_id->compiler_id + "==>" + compiler_grammar_file_name + " memory cache hint");
            compiler->languages.grammar_file_name = compiler_grammar_file_name;
            return compiler;
        }
        else
        {
            INFO(compiler_id->compiler_id + "==>" + compiler_grammar_file_name + " memory cache NOT hint");
        }
    }

    if (this->disk_cache_flag)
    {
        compiler = this->LoadCompiler(compiler_id);

        if (compiler != nullptr)
        {
            INFO(compiler_id->compiler_id + "==>" + compiler_grammar_file_name + " disk cache hint");
        }
        else
        {
            INFO(compiler_id->compiler_id + "==>" + compiler_grammar_file_name + " disk cache NOT hint");
        }
    }
    return compiler;
}

Compiler* CompilerManager::CreateCompiler(std::string compiler_grammar_file_name, Compiler* parent_compiler)
{
    Compiler* compiler = nullptr;
    if (compiler_grammar_file_name == GetBoostrapCompilerFileName())
    {
        //BoostrapCompiler只能通过BootstrapLoader加载创建
        compiler = new Compiler();
        if (!compiler->LoadLanguage(compiler_grammar_file_name))
        {
            INFO("load bootstrap compiler " + compiler_grammar_file_name + " FAILED!!!");
            delete compiler;
            compiler = nullptr;
        }
        else
        {
            INFO("load bootstrap compiler " + compiler_grammar_file_name + " SUCCESS!!!");
        }
    }
    else
    {
        //其他Compiler如果没有指定parent_compiler,尝试BootstrapCompiler加载
        if (parent_compiler == nullptr)
        {
            parent_compiler = this->CreateOrGetBoostrapCompiler();
        }

        ifstream input_grammar(compiler_grammar_file_name.c_str());
        if(!input_grammar)
        {
            ERROR("open gramar file["<<compiler_grammar_file_name<<"] failed");
            return nullptr;
        }
        bool retval = parent_compiler->Process(input_grammar, cerr);
        if (retval)
        {
            compiler = parent_compiler->interpreter->context.compiler;
        }
    }

    if (compiler != nullptr)
    {
        LanguageParsers* parser = this->CreateLanguageParser(this->language_parser_type, compiler);
        parser->Initialize();
        compiler->parser_strategy.Set(parser);

        compiler->compiler_id = *this->GetGrammarId(compiler_grammar_file_name);
        compiler->compiler_id.parent_compiler_id = parent_compiler== nullptr ? "" : parent_compiler->compiler_id.compiler_id;
        compiler->languages.grammar_file_name = compiler_grammar_file_name;

        CacheCompiler(compiler);
    }
    return compiler;
}

Compiler* CompilerManager::CreateOrGetCompiler(std::string compiler_grammar_file_name, Compiler* parent_compiler)
{
    Compiler* compiler = this->GetCompiler(compiler_grammar_file_name);
    if (compiler != nullptr)
    {
        return compiler;
    }
    else
    {
        return this->CreateCompiler(compiler_grammar_file_name, parent_compiler);
    }
}

void CompilerManager::CacheCompiler(Compiler *compiler, bool skip_disk_cache) {
    if (memory_cache_flag)
    {
        this->file_name_to_compiler_id.insert(make_pair(compiler->compiler_id.grammar_file_name, compiler->compiler_id));
        compiler_map.insert(pair<string,Compiler*>(compiler->compiler_id.compiler_id, compiler));
        INFO("add [" << compiler->compiler_id.compiler_id << "] into memory cache");
    }

    if (disk_cache_flag && !skip_disk_cache)
    {
        string cache_wll_file_name = this->GetCacheWllFileName(compiler->compiler_id.compiler_id);
        compiler->Save(cache_wll_file_name);
        this->SaveSymbols();
        INFO("add [" << compiler->compiler_id.compiler_id << "] into disk cache");
    }
}

LanguageParsers *CompilerManager::CreateLanguageParser(const string& language_parser_type, Compiler* compiler)
{
    LanguageParsers* parser = nullptr;

    if (language_parser_type == CompilerManager::TOPDOWNPARSER)
    {
        parser = new TopDownParsers(compiler);
    }
    else if (language_parser_type == CompilerManager::LR1PARSER)
    {
        parser = new LR1Parsers(compiler);
    }
    else
    {
        parser = new LR1Parsers(compiler);
    }

    return parser;
}

WllInterpreter *CompilerManager::CreateWllInterpreter(Compiler *compiler)
{
    WllInterpreter* interpreter = nullptr;

    if (compiler == nullptr)
    {
        switch (wll_interpreter_version)
        {
            case WLL2INTERPRETER:
                interpreter = Wll2Interpreter::GetDefaultWll2Interpreter();
                break;
            case WLL3INTERPRETER:
                interpreter = Wll3Interpreter::GetDefaultWll3Interpreter();
                break;
            default:
                interpreter = Wll3Interpreter::GetDefaultWll3Interpreter();
                break;
        }
    }
    else
    {
        switch (wll_interpreter_version)
        {
            case WLL2INTERPRETER:
                interpreter = new Wll2Interpreter(compiler);
                break;
            case WLL3INTERPRETER:
                interpreter = new Wll3Interpreter(compiler);
                break;
            default:
                interpreter = new Wll3Interpreter(compiler);
                break;
        }
    }

    return interpreter;
}

WllInterpreter *CompilerManager::GetOrCreateWllInterpreter(Compiler *compiler)
{
    if (compiler != nullptr && compiler->interpreter != nullptr) {
        return compiler->interpreter;
    }
    return CreateWllInterpreter(compiler);
}

Compiler *CompilerManager::LoadCompiler(CompilerId *compiler_id)
{
    Compiler* compiler = Compiler::Load(compiler_id);
    if (compiler != nullptr)
    {
        //cache into memory
        CacheCompiler(compiler, true);
    }
    return compiler;
}




